package com.nulldev.util.networking.rudp.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import com.nulldev.util.io.serialization.Serialization;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.manage.experiments.Experiments;

public class SerializationUtils {
	public static final boolean USE_NT_API = Experiments.get("rUDP://use-native-serialization/").isActive();
	private static final Serialization SE = USE_NT_API ? new Serialization() : null;
	private static SerializationUtils serializationUtils = null;
	private static final Logger LOG = LoggerFactory.getLogger();

	private SerializationUtils() {
	}

	public static SerializationUtils getInstance() {
		if (serializationUtils == null) {
			serializationUtils = new SerializationUtils();
		}
		return serializationUtils;
	}

	public <T> byte[] serialize(T obj) {
		if (USE_NT_API) {
			try {
				return SE.serialize(obj);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		} else {
			final ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = null;
			try {
				oos = new ObjectOutputStream(baos);
				oos.writeObject(obj);
				oos.reset();
			} catch (IOException e) {
				LOG.error(e);
			} finally {
				if (oos != null) {
					try {
						oos.close();
					} catch (IOException e) {
						LOG.error(e);
					}
				}
			}
			byte[] byteObj = baos.toByteArray();
			try {
				baos.close();
			} catch (IOException e) {
			}
			return byteObj;
		}
	}

	@SuppressWarnings("unchecked")
	public <T> T deserialize(byte[] bytes) {
		if (USE_NT_API) {
			try {
				return (T) SE.deserialize(bytes);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		} else {
			T obj = null;
			ByteArrayInputStream bais = null;
			ObjectInputStream ois = null;

			try {
				bais = new ByteArrayInputStream(bytes);
				ois = new ObjectInputStream(bais);
				obj = (T) ois.readObject();
			} catch (IOException e) {
				LOG.error(e);
			} catch (ClassNotFoundException e) {
				LOG.error(e);
				return null;
			} finally {
				if (ois != null) {
					try {
						ois.close();
					} catch (IOException e) {
						LOG.error(e);
					}
				}
			}
			return obj;
		}
	}
}
